﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ModbusCase.Lib
{
    public class Common
    {
        /// <summary>
        /// 生成CRC校验码
        /// </summary>
        /// <param name="data"></param>
        /// <returns>返回CRC校验码</returns>
        public static byte[] CRC16(List<byte> data)
        {
            //CRC校验的基本步骤如下：

            //1.预置：CRC寄存器预置(初始值)为0xFFFF。
            //2.数据输入：报文中除了CRC校验码以外的所有字节（包括设备地址、功能码和数据）按照顺序进行处理。
            //4.计算：对每一个字节，从最高位到最低位，将其与CRC寄存器当前的值进行异或运算。如果结果的最高位为1，则将寄存器的值左移一位并与0x8005进行异或运算；如果最高位为0，则只需左移一位。重复此过程，直至8位都处理完毕。然后继续处理下一个字节，直到所有字节都计算完毕。
            //4.结果：最后CRC寄存器中的值就是CRC校验码，通常在传输前转换为低字节在前（Little - Endian）的形式，并附加到报文的末尾。

            //当接收方收到报文时，会对整个报文（包括CRC校验码）使用相同的CRC计算流程。如果报文未被篡改，计算结果应为0x0000（考虑到了CRC码的加入和计算规则）。如果结果不是0x0000，则表明报文在传输过程中可能遭到了篡改或出现了错误。

            //运算
            ushort crc = 0xFFFF;
            for (int i = 0; i < data.Count; i++)
            {
                crc = (ushort)(crc ^ (data[i]));
                for (int j = 0; j < 8; j++)
                {
                    crc = (crc & 1) != 0 ? (ushort)((crc >> 1) ^ 0xA001) : (ushort)(crc >> 1);
                }
            }
            byte hi = (byte)((crc & 0xFF00) >> 8);  //高
            byte lo = (byte)(crc & 0x00FF);         //低

            return [lo, hi];
        }


        /// <summary>
        /// 错误码
        /// </summary>
        public static Dictionary<byte, string> ErrorCodes = new Dictionary<byte, string>
        {
            { 0x01, "非法的功能码"},
            { 0x02, "非法的数据地址"},
            { 0x03, "非法的数据值"},
            { 0x04, "从设备/服务器故障"},
            { 0x05, "确认，从设备/服务器需要一个耗时操作"},
            { 0x06, "从设备/服务器繁忙"},
            { 0x0A, "网关故障"},
            { 0x0B, "网关目标设备未能响应"},
        };
    }

    /// <summary>
    /// 大小端模式
    /// </summary>
    public enum Endian 
    {
        /// <summary>
        /// 大端模式
        /// </summary>
        BigEndian,
        /// <summary>
        /// 小端模式
        /// </summary>
        LittleEndian
    }

    /// <summary>
    /// 功能码
    /// </summary>
    public enum FuncCode
    {
        /// <summary>
        /// 读输出线圈
        /// </summary>
        ReadCoils = 0x01,
        /// <summary>
        /// 读(离散)输入线圈
        /// </summary>
        ReadDiscreteInputs = 0x02,
        /// <summary>
        /// 读保持寄存器
        /// </summary>
        ReadHoldingRegisters = 0x03,
        /// <summary>
        /// 读输入寄存器
        /// </summary>
        ReadInputRegisters = 0x04,
        /// <summary>
        /// 写单个线圈
        /// </summary>
        WriteSingleCoil = 0x05,
        /// <summary>
        /// 写单个寄存器
        /// </summary>
        WriteSingleRegister = 0x06,
        /// <summary>
        /// 写多个线圈
        /// </summary>
        WriteMultipleCoil = 0x0F,
        /// <summary>
        /// 写多个寄存器
        /// </summary>
        WriteMultipleRegisters = 0x10, 
    }
}
